package cn.blu10ph.trustshare.service;

import cn.blu10ph.trustshare.bean.node.NodeInfo;
import cn.blu10ph.trustshare.config.ServiceChannelInitializer;
import cn.blu10ph.trustshare.config.ServiceConfig;
import cn.blu10ph.trustshare.core.ServiceCore;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.FixedRecvByteBufAllocator;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;

/**
 * <p> DHTService </p >
 *
 * @author cxx
 * @date 2024/4/1 20:55
 */
@Slf4j
@Component
public class DHTStartService {

    @Resource
    ServiceConfig config;

    @Resource
    private ServiceChannelInitializer serverChannelInitializer;

    public InetSocketAddress addrPort(){
        return new InetSocketAddress(config.getServerIp(), config.getPort());
    }

    public EventLoopGroup eventLoopGroup(){
        return new NioEventLoopGroup();
    }

    public Bootstrap serviceBootstrap(EventLoopGroup eventLoopGroup, InetSocketAddress addrPort){
        return new Bootstrap()
                .group(eventLoopGroup)
                .channel(NioDatagramChannel.class)
                .option(ChannelOption.SO_BROADCAST, true)
                .option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(2*1024*1024))
                .handler(serverChannelInitializer)
                .localAddress(addrPort);
    }

    public void init(ApplicationArguments args) {
        log.info("service starting!");

        InetSocketAddress addrPort = addrPort();
        String host = addrPort.getHostString();
        if("0.0.0.0".equals(addrPort.getHostString())) {
            try {
                InetAddress localhost = InetAddress.getLocalHost();
                host = localhost.getHostAddress();
            } catch (UnknownHostException ex) {
                ex.printStackTrace();
            }
        }
        EventLoopGroup eventLoopGroup = eventLoopGroup();
        Bootstrap serviceBootstrap = serviceBootstrap(eventLoopGroup, addrPort);

        String nodeId = null;
        NodeInfo self = new NodeInfo(nodeId, host, config.getPort());
        try {
            ChannelFuture channel = serviceBootstrap.bind(config.getPort()).sync();
            ServiceCore.initService(self, channel);
            nodeId = self.getNodeId();
            log.info("service [{}] start in port:{}", nodeId, config.getPort());
            channel.channel().closeFuture().sync();
        } catch (Exception ex) {
            log.info("service exception:{}", ex.getMessage());
            ex.printStackTrace();
        } finally {
            log.info("service [{}] close!", nodeId);
            eventLoopGroup.shutdownGracefully();
        }
    }

}
